package com.zhike.service.group.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import com.zhike.common.enums.ResultCode;
import com.zhike.common.enums.YesNoEnum;
import com.zhike.common.exception.ZkimException;
import com.zhike.common.model.ChatSession;
import com.zhike.common.model.Device;
import com.zhike.common.model.GroupBase;
import com.zhike.common.model.GroupReq;
import com.aliyun.oss.ServiceException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.eva.epc.common.util.CommonUtils;
import com.google.gson.Gson;
import com.zhike.common.dto.CMDBody4DelGroupMember;
import com.zhike.common.dto.CMDBody4ExitGroup;
import com.zhike.common.dto.CMDBody4JoinToGroupNotify;
import com.zhike.common.dto.GroupDeleteMemberDTO;
import com.zhike.common.dto.GroupDissDTO;
import com.zhike.common.dto.GroupInfo4Cache;
import com.zhike.common.dto.GroupInviteJoinDTO;
import com.zhike.common.dto.GroupMember4Cache;
import com.zhike.service.group.IGroupMemberService;
import com.zhike.service.group.IGroupService;
import com.zhike.service.im.manager.ChatManager4Group;
import com.zhike.service.im.manager.GroupMessageManager;
import com.zhike.service.im.manager.StorageMessage;
import com.zhike.service.messages.IChatSessionService;
import com.zhike.service.scheduled.GroupProhibitScheduledService;
import com.zhike.service.user.IUserService;

import com.zhike.utils.GroupAvatarHelper;
import com.zhike.utils.SNHelper;
import com.zhike.utils.cache.GroupsInfoCacheProvider;
import com.zhike.utils.cache.GroupsMembersCacheProvider;
import com.zhike.utils.cache.UsersInfoCacheProvider;
import com.zhike.utils.manager.UsersStatusManager;

import lombok.extern.slf4j.Slf4j;

import com.zhike.mapper.group.GroupBaseMapper;
import com.zhike.mapper.group.GroupMemberMapper;
import com.zhike.mapper.group.GroupReqMapper;
import com.zhike.mobileimsdk.server.protocal.Protocal;
import com.zhike.common.dto.GroupMemberEntity;
import com.zhike.common.dto.GroupMemberProhibitDTO;
import com.zhike.common.dto.GroupNoticeDTO;
import com.zhike.common.dto.GroupProhibitDTO;
import com.zhike.common.dto.GroupReqDTO;
import com.zhike.common.dto.GroupSetAdminDTO;
import com.zhike.common.dto.GroupTransDTO;
import com.zhike.common.dto.GroupUserNicknameDTO;
import com.zhike.common.dto.GroupVerficateDTO;
import com.zhike.common.dto.MsgBody4Group;
import com.zhike.common.dto.MsgBodyGroupS2C;
import com.zhike.common.dto.MsgType;
import com.zhike.common.dto.GroupUpdateNameDTO;
import com.zhike.common.dto.GroupUserDTO;
import com.zhike.common.dto.GroupIdDTO;
import com.zhike.common.dto.UserProtocalsType;
import com.zhike.common.vo.GroupIdUserIdVO;
import com.zhike.common.vo.GroupMemberCountVO;
import com.zhike.common.vo.UserAvatarVO;
import com.zhike.common.vo.UserInfo4Cache;

@Service
@Slf4j
public class GroupServiceImpl implements IGroupService {

  /** 群组名称最大29个字符（中文算1个字符） */
  public final static int GROUP_NAME_MAX_LENGTH = 29;

  @Resource
  private GroupBaseMapper groupBaseMapper;
  @Resource
  private SNHelper snHelper;

  @Resource
  private UsersInfoCacheProvider usersInfoCacheProvider;
  @Resource
  private GroupsInfoCacheProvider groupsInfoCacheProvider;
  @Resource
  private GroupsMembersCacheProvider groupsMembersCacheProvider;

  @Autowired
  private ChatManager4Group chatManager4Group;

  @Resource
  private GroupMemberMapper groupMemeberMapper;

  @Resource
  private GroupReqMapper groupReqMapper;

  @Autowired
  private GroupAvatarHelper groupAvatarHelper;

  @Resource
  private IUserService userService;

  @Autowired
  private IChatSessionService chatSessionService;

  @Autowired
  private IGroupMemberService groupMemberService;

  @Autowired
  private UsersStatusManager usersStatusManager;

  @Override
  public List<GroupBase> queryGroupInfoList(String uId) {
    return groupBaseMapper.queryGroupInfoList(uId);
  }

  @Override
  public GroupBase queryGroupInfo(String gid, String myUserId) {
    GroupBase gb = groupBaseMapper.queryGroupInfo(gid, myUserId);
    ChatSession cs = chatSessionService.selectGroupSession(myUserId, gb.getGroupId());
    if (cs != null) {
      gb.setIsTop(cs.getIsTop());
      gb.setNodisturb(cs.getNodisturb());
    }
    return gb;
  }

  @Transactional
  public Boolean createGroup(GroupMemberEntity gme, ArrayList<GroupMemberEntity> newJoinMembers) {
    // 生成群ID
    String newGid = snHelper.getSN("group_base_g_id");
    if (StringUtils.isBlank(newGid)) {
      throw new ZkimException(ResultCode.GROUP_NO_ID);
    }
    // 成员列表中加入群主
    newJoinMembers.add(gme);

    try {
      // 插入群成员
      for (GroupMemberEntity entity : newJoinMembers) {
        Integer res = groupBaseMapper.prepareInsertMemberToGroup(entity.getUserId(), newGid, null, new Date(),
            entity.getNickname(),
            entity.getIsAdmin());
        if (res < 1) {
          throw new ZkimException(ResultCode.GROUP_ADD_MEMBER);
        }
      }

      Integer res = groupBaseMapper.insertGroupBase(newGid, YesNoEnum.YES.getValue(),
          constructNewGroupName(newJoinMembers),
          gme.getUserId(), newJoinMembers.size());
      if (res < 1) {
        throw new ZkimException(ResultCode.GROUP_CREATE);
      }

      // 生产群头像
      groupAvatarHelper.generateGroupAvatar(newGid);

      // 更新群缓存
      groupsInfoCacheProvider.reloadOneCache(newGid);
      groupsMembersCacheProvider.reloadOneCache(newGid);

      String sessionId = chatSessionService.initGroupSession(newGid);

      GroupInfo4Cache gc = groupsInfoCacheProvider.get(newGid);
      // String sessionId = chatSessionService.createSession(gme.getUserId(), newGid);
      // 通知群内所有人：群创建成功

      // 过滤掉群主
      ArrayList<GroupMemberEntity> membersNoWithOwner = new ArrayList<>();
      for (GroupMemberEntity entity : newJoinMembers) {
        if (entity.getUserId().equals(gme.getUserId())) {
          continue;
        }
        membersNoWithOwner.add(entity);
      }

      chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(newGid, gc.getGname()) {

        @Override
        public String buildGroupBody(MsgBody4Group root) {
          MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
          cmdBody.setUserId(gme.getUserId());
          cmdBody.setNickname(gme.getNickname());
          cmdBody.setGroupName(gc.getGname());
          cmdBody.setGroupId(gc.getGid());
          cmdBody.setCGroup(1);
          cmdBody.setMembers(membersNoWithOwner);

          root.setF("0");
          root.setT(gc.getGid());
          root.setTy(MsgType.TYPE_CREATEGROUP); // 创建群或邀请成员
          root.setM(new Gson().toJson(cmdBody));
          return new Gson().toJson(root);
        }

      }, new StorageMessage() {

        @Override
        public String getSessionId() {
          return sessionId;
        }

      });
    } catch (Exception e) {
      e.printStackTrace();
      throw new ZkimException(ResultCode.FAIL);
    }
    return true;
  }

  // 构造新的群名称
  private String constructNewGroupName(ArrayList<GroupMemberEntity> members) {
    if (members.size() > 0) {
      String name = "";

      int cnt = 0;

      for (GroupMemberEntity mem : members) {
        String nickname = "";
        UserInfo4Cache uc = usersInfoCacheProvider.get(mem.getUserId());
        if (uc != null) {
          nickname = uc.getNickName();
          mem.setNickname(nickname);
        } else {
          nickname = "N";
        }
        name += (cnt == 0 ? "" : "、") + nickname;
        cnt += 1;

        // 最多只合成3个人的昵称作为群名
        if (cnt >= 3)
          break;
      }

      log.debug("【截断前】生成完成的群聊名称：" + name);
      name = CommonUtils.truncString(name, GROUP_NAME_MAX_LENGTH, "...");
      log.debug("【截断后】生成完成的群聊名称：" + name);

      return name + "等";
    }

    return "未命名的群聊";
  }

  @Override
  public GroupMemberCountVO queryGroupMemberCount(String groupId) {
    return groupBaseMapper.queryGroupMemberCount(groupId);
  }

  @Override
  public List<UserAvatarVO> queryGroupUserHeadOrderNine(String groupId) {
    return groupBaseMapper.queryGroupUserHeadOrderNine(groupId);
  }

  @Override
  public Boolean updateGroupAvatarIncludeCnt(Integer size, String groupId) {
    return groupBaseMapper.updateGroupAvatarIncludeCnt(size, groupId) > 0;
  }

  @Override
  public Boolean updateGroupName(GroupUpdateNameDTO dto) {

    int res = groupBaseMapper.updateGroupName(dto.getGroupName(), dto.getDesc(), dto.getGroupId());

    if (res > 0) {
      // ** 【高速缓存】：即时更新群缓存数据（因为群名修改了）
      groupsInfoCacheProvider.reloadOneCache(dto.getGroupId());

      // ** 【实时通知本次群内的所有人】：群名修改了
      // chatLogicManager4Group.batchNotification4GroupNameChangedAsync(
      // dto.getModifyUid(),dto.getModifyNickname(), dto.getGroupName(),
      // dto.getGroupId());
      return Boolean.TRUE;
    }
    return Boolean.FALSE;
  }

  @Override
  public Boolean updateGroupUserNickName(String uid, GroupUserNicknameDTO dto) {
    int res = groupBaseMapper.updateGroupUserNickName(
        dto.getNicknameIngroup(), dto.getGroupId(), uid);
    if (res > 0) {
      groupsMembersCacheProvider.reloadOneCache(dto.getGroupId());
      return Boolean.TRUE;
    }
    return Boolean.FALSE;
  }

  @Override
  public Boolean updateGroupNotice(String uid, GroupNoticeDTO dto) {
    // 1.是否是群主
    if (!this.isGroupOwner(dto.getGroupId(), uid)) {
      throw new ZkimException(ResultCode.GROUP_NO_OWNER);
    }
    // 2.更新公告
    if (groupBaseMapper.updateGroupNotice(dto.getNotice(), uid,
        System.currentTimeMillis(),
        dto.getGroupId()) > 0) {
      return Boolean.TRUE;
    }
    return Boolean.FALSE;
  }

  /**
   * 用户是否是群主
   */
  @Override
  public Boolean isGroupOwner(String groupId, String uid) {
    GroupInfo4Cache gc = groupsInfoCacheProvider.get(groupId);
    if (gc != null && gc.getGOwnerUserUid().equals(uid)) {
      return Boolean.TRUE;
    }
    return Boolean.FALSE;
  }

  /**
   * 用户是否是群管理员
   * 
   * @return
   */
  @Override
  public Boolean isGroupAdmin(String groupId, String uid) {
    ConcurrentHashMap<String, GroupMember4Cache> members = groupsMembersCacheProvider.get(groupId);
    GroupMember4Cache gmc = null;
    Set<String> keySet = members.keySet();
    for (String key : keySet) {
      if (key.equals(uid)) {
        gmc = members.get(key);
        if (gmc.getIsAdmin().equals(YesNoEnum.YES.getValue())) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public Boolean deleteGroupPerson(UserInfo4Cache uc, GroupDeleteMemberDTO dto) {

    GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getGroupId());
    if (null == gc) {
      throw new ZkimException(ResultCode.GROUP_NO_EXIST);
    }
    GroupMember4Cache gmc = find(dto.getGroupId(), uc.getFakeUid());
    if (null == gmc) {
      throw new ZkimException(ResultCode.GROUP_NO_MEMBER);
    } else {
      Integer isAdmin = gmc.getIsAdmin();
      if (!this.isGroupOwner(dto.getGroupId(), uc.getFakeUid()) && !isAdmin.equals(YesNoEnum.YES.getValue())) {
        throw new ZkimException(ResultCode.GROUP_NORIGHT);
      }
    }

    ArrayList<GroupMemberEntity> membersBeDeleteList = new ArrayList<GroupMemberEntity>();
    for (GroupMemberEntity row : dto.getMembers()) {
      // 校验删除的成员中是否有群主
      if (this.isGroupOwner(dto.getGroupId(), row.getUserId())) {
        throw new ZkimException(ResultCode.GROUP_MEMBER_DEL_OWNER);
      }
      GroupMemberEntity gme = new GroupMemberEntity();
      gme.setUserId(row.getUserId());
      gme.setNickname(row.getNickname());

      membersBeDeleteList.add(gme);
      groupBaseMapper.deleteGroupPerson(dto.getGroupId(), row.getUserId());

      // 删除用户的会话
      chatSessionService.deleteSession(row.getUserId(), null, dto.getGroupId());
    }

    // 更新群成员人数
    groupBaseMapper.prepareUpdateGroupMemberCount(dto.getGroupId());

    // 生成群头像
    groupAvatarHelper.generateGroupAvatar(dto.getGroupId());

    // 发消息到群
    try {
      chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getGroupId(), gc.getGname()) {

        @Override
        public String buildGroupBody(MsgBody4Group root) {
          CMDBody4DelGroupMember entity = new CMDBody4DelGroupMember();
          entity.setNickname(uc.getNickName());
          entity.setUserId(uc.getFakeUid());

          boolean isAdmin = false;
          GroupMember4Cache gmc = find(dto.getGroupId(), uc.getFakeUid());
          if (null != gmc) {
            isAdmin = gmc.getIsAdmin().equals(YesNoEnum.YES.getValue());
          }

          if (isAdmin) {
            entity.setIsAdmin(YesNoEnum.YES.getValue());
          } else {
            entity.setIsAdmin(YesNoEnum.NO.getValue());
          }

          // 判断是否群主（只有群主 管理员才有权限）
          boolean isOwner = false;
          if (isGroupOwner(dto.getGroupId(), uc.getFakeUid())) {
            isOwner = true;
          }

          if (isOwner) {
            entity.setIsOwner(YesNoEnum.YES.getValue());
          } else {
            entity.setIsOwner(YesNoEnum.NO.getValue());
          }
          entity.setMembers(membersBeDeleteList);

          root.setF("0");
          root.setT(dto.getGroupId());
          root.setTy(MsgType.TYPE_DELMEM);
          root.setM(new Gson().toJson(entity));
          return new Gson().toJson(root);

        }

      }, new StorageMessage() {

        @Override
        public String getSessionId() {
          ChatSession cs = chatSessionService.selectGroupSession(uc.getFakeUid(), dto.getGroupId());
          if (null != cs) {
            return cs.getSessionId();
          }
          return "";
        }

      });

      // 更新缓存
      groupsMembersCacheProvider.reloadOneCache(dto.getGroupId());

    } catch (Exception e) {
      e.printStackTrace();
    }

    return Boolean.TRUE;
  }

  private GroupMember4Cache find(String groupId, String uid) {
    ConcurrentHashMap<String, GroupMember4Cache> members = groupsMembersCacheProvider.get(groupId);
    GroupMember4Cache gmc = null;
    Set<String> keySet = members.keySet();
    for (String key : keySet) {
      if (key.equals(uid)) {
        gmc = members.get(key);
        break;
      }
    }
    return gmc;
  }

  @Override
  public Boolean inviteToJoinTheGroup(String uid, String nickname, GroupInviteJoinDTO dto) {

    // 验证群是否存在
    GroupBase groupEntity = queryGroupInfo(dto.getInviteToGid(), uid);
    if (groupEntity == null) {
      throw new ZkimException(ResultCode.GROUP_NO_EXIST);
    }

    // 群成员数量校验
    Integer groupMemberCount = groupBaseMapper.queryGrouppersonCount(dto.getInviteToGid(), null);
    if (groupEntity.getMaxMemberCount() <= groupMemberCount) {
      throw new ZkimException(ResultCode.GROUP_MEMBER_MAX);
    }
    // 新成员列表
    ArrayList<GroupMemberEntity> newMemberList = new ArrayList<>();

    // 老群员列表
    Collection<GroupMember4Cache> oldMemberList = new ArrayList<>();

    ArrayList<GroupMemberEntity> all = new ArrayList<>();

    // 从高速缓存中读取群成员列表，提升性能
    oldMemberList = groupsMembersCacheProvider.get(dto.getInviteToGid()).values();
    // 校验邀请的新成员是否已在群内
    try {
      oldMemberList.forEach(member -> {
        if (dto.getMembers().stream().anyMatch(element -> element.getUserId().equals(member.getUserUid()))) {
          throw new ZkimException("该用户已加入群聊");
        } else {
          GroupMemberEntity entity = new GroupMemberEntity();
          entity.setUserId(member.getUserUid());
          all.add(entity);
        }
      });
    } catch (ZkimException e) {
      throw new ZkimException(ResultCode.GROUP_NO_REPEAT_INVITE);
    }

    // 持久化新群员
    for (GroupMemberEntity row : dto.getMembers()) {
      // 将群员的基本信息存到一个集合中备用
      GroupMemberEntity newGme = new GroupMemberEntity();
      // 校验用户 (从缓存中查询)
      UserInfo4Cache userCache = usersInfoCacheProvider.get(row.getUserId());
      if (userCache != null) {
        newGme.setNickname(userCache.getNickName());
        newGme.setUserId(row.getUserId());
        newMemberList.add(newGme);
        all.add(newGme);
      }
    }

    GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getInviteToGid());

    // 加入群，无需校验
    if (groupEntity.getGroupingVerification().equals(YesNoEnum.NO.getValue())) {
      for (GroupMemberEntity row : newMemberList) {
        UserInfo4Cache userCache = usersInfoCacheProvider.get(row.getUserId());
        if (userCache != null) {
          // 保存新群成员
          int res = groupBaseMapper.prepareInsertMemberToGroup(row.getUserId(), dto.getInviteToGid(),
              uid, null,
              userCache.getNickName(),
              YesNoEnum.NO.getValue());
          if (res > 0) {
            chatSessionService.initGroupMemberSession(dto.getInviteToGid(), row.getUserId());
          }
        }
      }

      // 更新群成员总人数
      groupBaseMapper.prepareUpdateGroupMemberCount(dto.getInviteToGid());

      // 生成群头像
      groupAvatarHelper.generateGroupAvatar(dto.getInviteToGid());

      groupsMembersCacheProvider.reloadOneCache(dto.getInviteToGid());

      // 发消息通知
      try {
        chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getInviteToGid(), gc.getGname()) {

          @Override
          public String buildGroupBody(MsgBody4Group root) {
            MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
            cmdBody.setUserId(uid);
            cmdBody.setNickname(nickname);
            cmdBody.setGroupName(gc.getGname());
            cmdBody.setGroupId(gc.getGid());
            if (dto.getSrcFrom().equals("1")) {
              cmdBody.setCGroup(13);
            } else if (dto.getSrcFrom().equals("3")) {
              cmdBody.setCGroup(14); // 自主入群
            } else {
              cmdBody.setCGroup(1);
            }
            cmdBody.setMembers(newMemberList);

            root.setF("0");
            root.setT(gc.getGid());
            root.setTy(MsgType.TYPE_CREATEGROUP); // 创建群或邀请成员
            root.setM(new Gson().toJson(cmdBody));
            return new Gson().toJson(root);
          }

        }, new StorageMessage() {

          @Override
          public String getSessionId() {
            return chatSessionService.createSession(uid, dto.getInviteToGid());
          }

        });
      } catch (Exception e) {
        e.printStackTrace();
      }

    } else if (groupEntity.getGroupingVerification().equals(YesNoEnum.YES.getValue())) { // 入群审核

      if (dto.getMembers().size() == 0) {
        throw new ZkimException(ResultCode.SYS_ARG_VALID);
      }

      boolean flag = false;

      for (GroupMemberEntity member : dto.getMembers()) {

        GroupReq gr = new GroupReq();
        gr.setGId(dto.getInviteToGid());
        gr.setBeDesc(dto.getDesc());
        gr.setBeUserUid(member.getUserId());
        gr.setUserUid(uid);
        gr.setBeTimestamp(System.currentTimeMillis());
        gr.setStatus(YesNoEnum.NO.getValue());
        flag = groupReqMapper.insert(gr) > 0;
        if (!flag) {
          break;
        }
      }

      if (!flag) {
        log.warn("邀请群成员时发生错误 操作:{}-{} 群ID:{}", uid, nickname, dto.getInviteToGid());
        return Boolean.FALSE;
      }

      try {
        chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getInviteToGid(), "") {

          @Override
          public List<String> targetDevices() {
            List<String> deviceNoList = new ArrayList<>();
            List<GroupMemberEntity> list = groupBaseMapper.queryAdminByGId(dto.getInviteToGid());
            for (GroupMemberEntity entity : list) {
              UserInfo4Cache uc = usersInfoCacheProvider.get(entity.getUserId());
              List<Device> deviceList = uc.getDeviceList();
              for (Device device : deviceList) {
                if (usersStatusManager.isOnline(device.getDeviceNo())) {
                  deviceNoList.add(device.getDeviceNo());
                }
              }
            }
            return deviceNoList;
          }

          @Override
          public String buildGroupBody(MsgBody4Group root) {
            MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
            cmdBody.setUserId(uid);
            cmdBody.setNickname(nickname);
            cmdBody.setGroupName(gc.getGname());
            cmdBody.setGroupId(gc.getGid());
            cmdBody.setCGroup(1);
            cmdBody.setMembers(dto.getMembers());

            root.setF("0");
            root.setT(gc.getGid());
            root.setTy(MsgType.TYPE_CREATEGROUP);
            root.setM(new Gson().toJson(cmdBody));
            return new Gson().toJson(root);
          }

        }, null);
      } catch (Exception e) {
        e.printStackTrace();
      }

      if (!this.isGroupOwner(dto.getInviteToGid(), uid) && !this.isGroupAdmin(dto.getInviteToGid(), uid)) {
        try {
          chatManager4Group.notify4Group(new GroupMessageManager(dto.getInviteToGid(), "") {
            @Override
            public String buildNotificationBody() {
              CMDBody4JoinToGroupNotify cmdBody = new CMDBody4JoinToGroupNotify();
              cmdBody.setGroupId(dto.getInviteToGid());
              cmdBody.setNotifyCount(dto.getMembers().size());
              return new Gson().toJson(cmdBody);
            }

            @Override
            public List<GroupMember4Cache> targetDevicesForNotify() {
              return getAdminUsers(dto.getInviteToGid());
            }

          }, UserProtocalsType.MT46_OF_GROUP$SYSCMD_MYSELF$BE$INVITE_FROM$SERVER,
              dto.getMembers().size());
        } catch (Exception e) {
          e.printStackTrace();
        }
      }

    }

    return Boolean.TRUE;
  }

  @Override
  public Boolean transferenceGroup(String uid, String nickname, GroupTransDTO dto) {
    if (groupBaseMapper.queryGroupOwner(dto.getGroupId(), dto.getOldOwnerUid()) < 1) {
      throw new ZkimException(ResultCode.GROUP_NO_OWNER);
    }

    if (groupBaseMapper.queryGrouppersonCount(dto.getGroupId(), dto.getNewOwnerUid()) <= 0) {
      throw new ZkimException(ResultCode.GROUP_NO_MEMBER);
    } else {
      Integer f1 = groupBaseMapper.updateGroupMaster(dto.getNewOwnerUid(), dto.getGroupId(), dto.getOldOwnerUid());

      Integer f2 = groupBaseMapper.updateGroupMasterTime(dto.getGroupId(), dto.getNewOwnerUid());

      if (f1 > 0 && f2 > 0) {
        // ** 【高速缓存】：将最新群成员数据更新到高速缓存中（以便后绪使用）
        groupsMembersCacheProvider.reloadOneCache(dto.getGroupId());

        GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getGroupId());
        try {
          chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getGroupId(), gc.getGname()) {

            @Override
            public String buildGroupBody(MsgBody4Group root) {
              ArrayList<GroupMemberEntity> memList = new ArrayList<>();
              GroupMemberEntity mem = new GroupMemberEntity();
              mem.setUserId(dto.getNewOwnerUid());
              UserInfo4Cache uc = usersInfoCacheProvider.get(dto.getNewOwnerUid());
              mem.setNickname(uc.getNickName());
              memList.add(mem);

              MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
              cmdBody.setUserId(uid);
              cmdBody.setNickname(nickname);
              cmdBody.setGroupName(gc.getGname());
              cmdBody.setGroupId(dto.getGroupId());
              cmdBody.setCGroup(9);
              cmdBody.setMembers(memList);

              root.setF("0");
              root.setT(dto.getGroupId());
              root.setTy(MsgType.TYPE_TRANS);
              root.setM(new Gson().toJson(cmdBody));
              return new Gson().toJson(root);
            }

          }, new StorageMessage() {

            @Override
            public String getSessionId() {
              ChatSession cs = chatSessionService.selectGroupSession(uid, dto.getGroupId());
              if (null != cs) {
                return cs.getSessionId();
              }
              return "";
            }

          });
        } catch (Exception e) {
          e.printStackTrace();
        }

        groupsInfoCacheProvider.reloadOneCache(dto.getGroupId());
      }
    }
    return Boolean.TRUE;
  }

  @Override
  @Transactional
  public Boolean dissolutionGroup(String uid, String nickname, GroupDissDTO dto) {
    if (!this.isGroupOwner(dto.getGroupId(), uid)) {
      throw new ZkimException(ResultCode.GROUP_NO_OWNER);
    }

    // ** 再删除所有成员
    Integer f1 = groupBaseMapper.deleteGroupAllPersonByGroupId(dto.getGroupId());

    Integer f2 = groupBaseMapper.updateGroupBaseStatus(uid, dto.getGroupId());

    // 更新群成员人数
    Integer f3 = groupBaseMapper.prepareUpdateGroupMemberCount(dto.getGroupId());

    boolean f4 = chatSessionService.deleteGroupAllSession(dto.getGroupId());
    boolean res = f1 > 0 && f2 > 0 && f3 > 0 && f4;

    if (res) {
      try {
        chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getGroupId(), "") {

          GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getGroupId());

          @Override
          public String buildGroupBody(MsgBody4Group root) {
            MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
            cmdBody.setUserId(uid);
            cmdBody.setNickname(nickname);
            cmdBody.setGroupName(gc.getGname());
            cmdBody.setGroupId(dto.getGroupId());
            cmdBody.setCGroup(10);

            root.setF("0");
            root.setT(dto.getGroupId());
            root.setTy(MsgType.TYPE_DISBAND);
            root.setM(new Gson().toJson(cmdBody));
            return new Gson().toJson(root);
          }

        }, null);
      } catch (Exception e) {
        e.printStackTrace();
      }

      groupsMembersCacheProvider.remove(dto.getGroupId());
      groupsInfoCacheProvider.remove(dto.getGroupId());
    }

    return Boolean.TRUE;
  }

  @Override
  public List<GroupMemberEntity> queryGroupPersonList(String uid, GroupIdDTO dto) {
    return groupBaseMapper.queryGroupMemberList(dto.getGroupId(), uid);
  }

  @Override
  public Integer queryGroupRequestCountByUidFriendUid(String gId, String friendUserUid) {
    return groupBaseMapper.queryGroupRequestCountByUidFriendUid(gId, friendUserUid);
  }

  @Override
  public Boolean updateGroupRequestTime(String desc, Long time, String uid, String friendUid, String gId) {
    return groupBaseMapper.updateGroupRequestTime(desc, time, uid, friendUid, gId) > 0;
  }

  @Override
  public Boolean insertGroupRequestData(String uid, String friendUid, String desc, String ip, Long time, String gId) {
    return groupBaseMapper.insertGroupRequestData(uid, friendUid, desc, ip, time, gId) > 0;
  }

  @Override
  public List<GroupIdUserIdVO> queryGroupsAllData() {
    return groupBaseMapper.queryGroupAllData();
  }

  @Override
  @Transactional
  public Boolean setAdministrator(String uid, String nickname, GroupSetAdminDTO dto) {

    if (!this.isGroupOwner(dto.getGroupId(), uid)) {
      throw new ZkimException(ResultCode.GROUP_NO_OWNER);
    }

    GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getGroupId());

    ArrayList<GroupMemberEntity> memberList = new ArrayList<>();
    for (GroupUserDTO person : dto.getTargetUsers()) {
      if (StringUtils.isEmpty(person.getUserId()) || StringUtils.isEmpty(person.getNickname())) {
        throw new ServiceException("群成员缺少参数！");
      }
      Integer res = groupBaseMapper.updateGroupAdminPerson(dto.getIsAdmin(), dto.getGroupId(), person.getUserId());
      if (res <= 0) {
        throw new ZkimException("修改群管理失败！");
      }
      GroupMemberEntity entity = new GroupMemberEntity();
      entity.setUserId(person.getUserId());
      entity.setNickname(person.getNickname());
      memberList.add(entity);
    }

    groupsMembersCacheProvider.reloadOneCache(dto.getGroupId());

    try {
      chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getGroupId(), gc.getGname()) {

        @Override
        public String buildGroupBody(MsgBody4Group root) {

          MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
          cmdBody.setUserId(uid);
          cmdBody.setNickname(nickname);
          cmdBody.setGroupName(gc.getGname());
          cmdBody.setGroupId(dto.getGroupId());
          cmdBody.setCGroup(6);
          cmdBody.setMembers(memberList);
          cmdBody.setIsAdmin(dto.getIsAdmin());

          root.setF("0");
          root.setT(dto.getGroupId());
          root.setTy(MsgType.TYPE_SETADMIN);
          root.setM(new Gson().toJson(cmdBody));
          return new Gson().toJson(root);

        }

      }, new StorageMessage() {

        @Override
        public String getSessionId() {
          ChatSession cs = chatSessionService.selectGroupSession(uid, dto.getGroupId());
          if (null != cs) {
            return cs.getSessionId();
          }
          return "";
        }

      });
    } catch (Exception e) {
      e.printStackTrace();
    }
    return true;
  }

  @Override
  @Transactional
  public Boolean exitGroup(UserInfo4Cache uc, GroupIdDTO dto) {
    // 判断是否群主（只有群主才有权限）
    if (this.isGroupOwner(dto.getGroupId(), uc.getFakeUid())) {
      throw new ZkimException(ResultCode.GROUP_MASTER_EXIT);
    }

    GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getGroupId());
    if (null == gc) {
      throw new ZkimException(ResultCode.GROUP_NO_EXIST);
    }

    try {
      ArrayList<GroupMemberEntity> membersBeDeleteList = new ArrayList<GroupMemberEntity>();

      GroupMemberEntity gme = new GroupMemberEntity();
      gme.setUserId(uc.getFakeUid());
      membersBeDeleteList.add(gme);
      groupBaseMapper.deleteGroupPerson(dto.getGroupId(), uc.getFakeUid());

      // 删除会话
      chatSessionService.deleteSession(uc.getFakeUid(), null, dto.getGroupId());

      // 更新群成员人数
      groupBaseMapper.prepareUpdateGroupMemberCount(dto.getGroupId());

      // 生成群头像
      groupAvatarHelper.generateGroupAvatar(dto.getGroupId());

      // 更新缓存
      groupsMembersCacheProvider.reloadOneCache(dto.getGroupId());

      // 发消息到群
      chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getGroupId(), gc.getGname()) {

        @Override
        public String buildGroupBody(MsgBody4Group root) {
          CMDBody4ExitGroup entity = new CMDBody4ExitGroup();
          entity.setNickname(uc.getNickName());
          entity.setUserId(uc.getFakeUid());

          root.setT(dto.getGroupId());
          root.setTy(MsgType.TYPE_EXITGROUP);
          root.setM(new Gson().toJson(entity));
          root.setM2(uc.getCurrentDeviceId());
          return new Gson().toJson(root);
        }

      }, null);

    } catch (Exception e) {
      e.printStackTrace();
    }

    return Boolean.TRUE;
  }

  @Override
  public Boolean setAllProhibitMsg(String uid, String nickname, GroupProhibitDTO dto) {
    if (!isGroupOwner(dto.getGroupId(), uid)) {
      throw new ZkimException(ResultCode.GROUP_NO_OWNER);
    }
    LambdaUpdateWrapper<GroupBase> luw = Wrappers.lambdaUpdate();
    luw.eq(GroupBase::getGroupId, dto.getGroupId());
    luw.eq(GroupBase::getStatus, YesNoEnum.YES.getValue());
    luw.set(GroupBase::getAllPersonProhibitMsg, dto.getAction());
    if (groupBaseMapper.update(new GroupBase(), luw) <= 0) {
      throw new ZkimException(ResultCode.DB_ERROR);
    }

    groupsInfoCacheProvider.reloadOneCache(dto.getGroupId());

    GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getGroupId());
    try {
      chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getGroupId(), gc.getGname()) {

        @Override
        public String buildGroupBody(MsgBody4Group root) {
          MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
          cmdBody.setUserId(uid);
          cmdBody.setNickname(nickname);
          if (dto.getAction().equals(YesNoEnum.YES.getValue())) {
            cmdBody.setCGroup(8);
          } else if (dto.getAction().equals(YesNoEnum.NO.getValue())) {
            cmdBody.setCGroup(81);
          }

          root.setF("0");
          root.setT(dto.getGroupId());
          root.setTy(MsgType.TYPE_PROHIBIT);
          root.setM(new Gson().toJson(cmdBody));
          return new Gson().toJson(root);
        }

      }, new StorageMessage() {

        @Override
        public String getSessionId() {
          ChatSession cs = chatSessionService.selectGroupSession(uid, dto.getGroupId());
          if (null != cs) {
            return cs.getSessionId();
          }
          return "";
        }

      });
    } catch (Exception e) {
      e.printStackTrace();
    }

    return Boolean.TRUE;
  }

  @Override
  public Boolean prohibitMembers(String uid, String nickname, GroupMemberProhibitDTO dto) {

    if (dto.getAction().equals(YesNoEnum.YES.getValue())) {
      if (dto.getInterval() < -1) {
        throw new ZkimException(ResultCode.GROUP_PROHIBIT_INTERVAL);
      }
    }
    GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getGroupId());

    ScheduledFuture sf = null;
    for (GroupMemberEntity entity : dto.getMembers()) {
      if (groupMemberService.prohibitMember(dto.getGroupId(), entity.getUserId(), dto.getAction(), dto.getInterval())) {
        // if (dto.getAction().equals(YesNoEnum.YES.getValue()) && dto.getInterval() >
        // 0) {
        // sf = groupProhibitScheduledService.startTask(dto.getGroupId(),
        // entity.getUserId());
        // } else if (dto.getAction().equals(YesNoEnum.NO.getValue())) {
        // groupProhibitScheduledService.stopTask();
        // }

      }
    }

    groupsMembersCacheProvider.reloadOneCache(dto.getGroupId());

    try {
      chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getGroupId(), gc.getGname()) {

        @Override
        public String buildGroupBody(MsgBody4Group root) {
          MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
          cmdBody.setUserId(uid);
          cmdBody.setNickname(nickname);
          if (dto.getAction().equals(YesNoEnum.YES.getValue())) {
            cmdBody.setCGroup(4);
          } else if (dto.getAction().equals(YesNoEnum.NO.getValue())) {
            cmdBody.setCGroup(41);
          }
          cmdBody.setMembers(dto.getMembers());

          root.setF("0");
          root.setT(dto.getGroupId());
          root.setTy(MsgType.TYPE_PROHIBIT);
          root.setM(new Gson().toJson(cmdBody));
          return new Gson().toJson(root);
        }

      }, new StorageMessage() {

        @Override
        public String getSessionId() {
          ChatSession cs = chatSessionService.selectGroupSession(uid, dto.getGroupId());
          if (null != cs) {
            return cs.getSessionId();
          }
          return "";
        }

      });
    } catch (Exception e) {
      e.printStackTrace();
    }

    return Boolean.TRUE;
  }

  @Override
  public Boolean updateGroupVerficate(String uid, GroupVerficateDTO dto) {
    if (!isGroupOwner(dto.getGroupId(), uid)) {
      throw new ZkimException(ResultCode.GROUP_NO_OWNER);
    }
    LambdaUpdateWrapper<GroupBase> luw = new LambdaUpdateWrapper<>();
    luw.eq(GroupBase::getGroupId, dto.getGroupId());
    luw.set(GroupBase::getGroupingVerification, dto.getAction());
    return groupBaseMapper.update(new GroupBase(), luw) > 0;
  }

  @Override
  public List<GroupReq> inviteToGroupReqList(String uid, String groupId) {
    if (!isGroupAdmin(groupId, uid) && !isGroupOwner(groupId, uid)) {
      throw new ZkimException(ResultCode.GROUP_NORIGHT);
    }

    LambdaQueryWrapper<GroupReq> lqw = new LambdaQueryWrapper<>();
    lqw.eq(GroupReq::getGId, groupId);
    return groupReqMapper.selectList(lqw);
  }

  @Override
  @Transactional
  public Boolean agree(String uid, String nickname, GroupReqDTO dto) {
    if (!isGroupAdmin(dto.getGroupId(), uid) && !isGroupOwner(dto.getGroupId(), uid)) {
      throw new ZkimException(ResultCode.GROUP_NORIGHT);
    }

    GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getGroupId());
    if (gc.getGMemberCount() >= gc.getMaxMemberCount()) {
      throw new ZkimException(ResultCode.GROUP_MEMBER_MAX);
    }

    boolean send = false;

    try {
      for (GroupMemberEntity entity : dto.getMembers()) {
        GroupMember4Cache gmc = find(dto.getGroupId(), entity.getUserId());
        if (gmc != null) {
          groupReqMapper.deleteById(dto.getId());
          continue;
        }
        Integer res = groupBaseMapper.prepareInsertMemberToGroup(entity.getUserId(), dto.getGroupId(), uid,
            new Date(),
            entity.getNickname(),
            YesNoEnum.NO.getValue());
        if (res > 0) {
          groupReqMapper.deleteById(dto.getId());
          groupBaseMapper.prepareUpdateGroupMemberCount(dto.getGroupId());
          groupsMembersCacheProvider.reloadOneCache(dto.getGroupId());
          chatSessionService.initGroupMemberSession(dto.getGroupId(), entity.getUserId());

          send = true;
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }

    if (send) {
      try {
        chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getGroupId(), gc.getGname()) {

          @Override
          public String buildGroupBody(MsgBody4Group root) {
            UserInfo4Cache uc = usersInfoCacheProvider.get(dto.getUserId());
            MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
            cmdBody.setUserId(uc.getFakeUid());
            cmdBody.setNickname(uc.getNickName());
            cmdBody.setGroupName(gc.getGname());
            cmdBody.setGroupId(gc.getGid());
            cmdBody.setCGroup(11);
            cmdBody.setMembers(dto.getMembers());

            root.setF("0");
            root.setT(gc.getGid());
            root.setTy(MsgType.TYPE_CREATEGROUP);
            root.setM(new Gson().toJson(cmdBody));
            return new Gson().toJson(root);
          }

        }, new StorageMessage() {

          @Override
          public String getSessionId() {
            ChatSession cs = chatSessionService.selectGroupSession(uid, dto.getGroupId());
            if (null != cs) {
              return cs.getSessionId();
            }
            return "";
          }

        });
      } catch (Exception e) {
        e.printStackTrace();
      }
    }

    return Boolean.TRUE;
  }

  @Override
  public Boolean refuse(String uid, String nickname, GroupReqDTO dto) {
    if (!isGroupAdmin(dto.getGroupId(), uid) && !isGroupOwner(dto.getGroupId(), uid)) {
      throw new ZkimException(ResultCode.GROUP_NORIGHT);
    }

    GroupInfo4Cache gc = groupsInfoCacheProvider.get(dto.getGroupId());

    if (groupReqMapper.deleteById(dto.getId()) > 0) {
      try {
        chatManager4Group.sendMsgOrSave4Group(new GroupMessageManager(dto.getGroupId(), "") {

          @Override
          public String buildGroupBody(MsgBody4Group root) {
            UserInfo4Cache uc = usersInfoCacheProvider.get(dto.getUserId());
            MsgBodyGroupS2C cmdBody = new MsgBodyGroupS2C();
            cmdBody.setUserId(uc.getFakeUid());
            cmdBody.setNickname(uc.getNickName());
            cmdBody.setGroupName(gc.getGname());
            cmdBody.setGroupId(gc.getGid());
            cmdBody.setCGroup(12);
            cmdBody.setMembers(dto.getMembers());

            root.setF("0");
            root.setT(gc.getGid());
            root.setTy(MsgType.TYPE_CREATEGROUP);
            root.setM(new Gson().toJson(cmdBody));
            return new Gson().toJson(root);
          }

        }, null);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }

    return Boolean.TRUE;
  }

  /**
   * 获取群管理员包括群主
   * 
   * @return
   */
  public List<GroupMember4Cache> getAdminUsers(String groupId) {
    ConcurrentHashMap<String, GroupMember4Cache> members = groupsMembersCacheProvider.get(groupId);
    List<GroupMember4Cache> adminUsers = new ArrayList<>();
    Set<String> keySet = members.keySet();
    GroupInfo4Cache gc = groupsInfoCacheProvider.get(groupId);
    for (String key : keySet) {
      GroupMember4Cache gmc = members.get(key);
      if (gmc.getIsAdmin().equals(YesNoEnum.YES.getValue())
          || gmc.getUserUid().equals(gc.getGOwnerUserUid())) {
        adminUsers.add(gmc);
      }
    }

    return adminUsers;
  }

  @Override
  public List<String> getAdminUsersIds(String groupId) {
    List<String> uids = new ArrayList<>();
    ConcurrentHashMap<String, GroupMember4Cache> members = groupsMembersCacheProvider.get(groupId);

    Set<String> keySet = members.keySet();
    GroupInfo4Cache gc = groupsInfoCacheProvider.get(groupId);
    for (String key : keySet) {
      GroupMember4Cache gmc = members.get(key);
      if (gmc.getIsAdmin().equals(YesNoEnum.YES.getValue())
          || gmc.getUserUid().equals(gc.getGOwnerUserUid())) {
        uids.add(gmc.getUserUid());
      }
    }

    return uids;
  }
}
